﻿#define _DEBUG_MODE_
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace PHPDecoder
{

    public enum PHPKEYWORDS
    {
        PHPSTART, // <?[PHP]
        PUBLICMODIFIER, // "public ", "private", "static", "protected", "abstract"
        PRIVATEMODIFIER,
        STATICMODIFIER,
        PROTECTEDMODIFIER,
        ABSTRACTMODIFIER,
        CLASS,
        INTERFACE,
        METHOD, //function name(parameters)                                      "function "
        VARIABLE, // $                                                           "$"
        GLOBAL, //                                                               "global"
        TRY, // try {statement}catch(Exception $varname){                        "try"
        THROW, //throw expr;                                                     "throw"
        IF, //if (expr) {truestatement} [else { falsestatement }]                "if"
        WHILE, // while (expr){statement;}                                       "while"
        DO, //do {statement} while (expr);                                       "do"
        FOR, // for (intexpr; condexpr; increxpr){statement;}                    "for"
        FOREACH, // foreach(expr )                                               "foreach"
        SWITCH, // switch (expr) {[expr] statement* };                           "switch"
        PHPEND, // ?>                                                             "?>"
        RETURN,
        OTHERS,//function(parameters);
        COMMENTS_1, // //
        COMMENTS_2, // /*
        INCLUDE, // include()
        INCLUDE_ONCE,
        REQUIRE,
        REQUIRE_ONCE,
        CASE,
        BREAK,
        DEFAULT,
        VAR, //var $x;
        ELSE,
        ELSEIF,
        ECHO,
        NOP
    }


    public enum OPERANDTP
    {
        VARIABLE, FUNCTION, CONSTANT
    }

    /// <summary>
    /// Trieda reprezentujuca vysledku analyzy zdrojovych kodov
    /// </summary>
    [Serializable]
    public class AnalyseResult
    {
        private Dictionary<String, ClassNode> clss = new Dictionary<String, ClassNode>();
        public Dictionary<string, List<DataNode>> decodedFile = new Dictionary<string, List<DataNode>>();
        public AnalyseResult()
        {
            clss.Add("global", new ClassNode("global"));
        }
        
        public static void Save(String path, AnalyseResult res)
        {
            FileStream fs = null;
            BinaryFormatter bf = null;

            try
            {
                fs = new FileStream(path, FileMode.Create, FileAccess.Write);
                bf = new BinaryFormatter();
                bf.Serialize(fs, res);
            }
            catch (Exception)
            {
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                    fs.Dispose();
                }
            }
        }

        public static void Load(String path, ref AnalyseResult res)
        {
            FileStream fs = null;
            BinaryFormatter bf = null;

            try
            {
                fs = new FileStream(path, FileMode.Open, FileAccess.Read);
                bf = new BinaryFormatter();
                res = (AnalyseResult)bf.Deserialize(fs);
            }
            catch (FileNotFoundException)
            {
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                    fs.Dispose();
                }
            }
        }

        public void Add(ClassNode item)
        {
            if (clss.ContainsKey(item.name)) return;
            clss.Add(item.name, item);
        }

        public void Add(ClassNode cls, FunctionNode item)
        {
            cls.Add(item);
        }

        public void Add(ClassNode cls, VariableNode item)
        {
            cls.Add(item);
        }

        public Dictionary<String, ClassNode>.ValueCollection getAllClasses()
        {
            return clss.Values;
        }

        public ClassNode getClass(String name)
        {
            if (name == null) return clss["global"];
            if (this.clss.ContainsKey(name))
            {
                return clss[name];
            }
            ClassNode n = new ClassNode(name);
            clss.Add(name, n);
            return n;
        }

        public ClassNode getGlobalClass()
        {
            return clss["global"];
        }

        public List<FunctionNode> getAllFunctionsSortedByCall()
        {
            List<FunctionNode> l = new List<FunctionNode>();
            foreach (ClassNode cls in clss.Values)
            {
                foreach (FunctionNode f in cls.getFunctions().Values)
                {
                    f.inCls = cls;
                    l.Add(f);
                }
            }
            l.Sort((FunctionNode n1, FunctionNode n2) => Comparer<int>.Default.Compare(n1.call, n2.call));
            return l;
        }
        
        public List<VariableNode> getAllVarsSortedByRead()
        {
            List<VariableNode> l = new List<VariableNode>();
            foreach (ClassNode cls in clss.Values)
            {
                foreach (VariableNode v in cls.getVariables().Values)
                {
                    l.Add(v);
                }
            }
            l.Sort((VariableNode v1, VariableNode v2) => Comparer<int>.Default.Compare(v1.read, v2.read));
            return l;
        }
        public List<VariableNode> getAllVarsSortedByWrite()
        {
            List<VariableNode> l = new List<VariableNode>();
            foreach (ClassNode cls in clss.Values)
            {
                foreach (VariableNode v in cls.getVariables().Values)
                {
                    l.Add(v);
                }
            }
            l.Sort((VariableNode v1, VariableNode v2) => Comparer<int>.Default.Compare(v1.write, v2.write));
            return l;
        }
        public List<VariableNode> getAllVarsSortedByChange()
        {
            List<VariableNode> l = new List<VariableNode>();
            foreach (ClassNode cls in clss.Values)
            {
                foreach (VariableNode v in cls.getVariables().Values)
                {
                    l.Add(v);
                }
            }
            l.Sort((VariableNode v1, VariableNode v2) => Comparer<int>.Default.Compare(v1.change, v2.change));
            return l;
        }
        //public FunctionNode getFunction(String function, int paramNum)
        //{
        //    // function je v tvare trieda->funkcia.
        //    if (function.IndexOf("->") != -1)
        //    {
        //        String clsName = function.Substring(0, function.IndexOf("->"));
        //        String funcName = function.Substring(function.IndexOf("->") + 2);
        //        if (clss.ContainsKey(clsName))
        //        {
        //            Dictionary<String, FunctionNode> funcs = clss[clsName].getFunctions();
        //            if (funcs.ContainsKey(funcName))
        //            {
        //                return funcs[funcName];
        //            }
        //        }
        //        return null;
        //    }
        //    else
        //    {

        //    }
        //    return null;
        //}
    }

    public enum NodeType
    {
        CLASS, METHOD, VAR
    }

    [Serializable]
    public class DataNode
    {
        public NodeType tp;
        public bool isoutput = true;
        public String name = null;
    }

    /// <summary>
    /// Trieda reprezentujuca triedy v zdrojovych kodoch
    /// </summary>
    [Serializable]
    public class ClassNode : DataNode
    {
        public String path = null;
        public Dictionary<String, FunctionNode> functions = null;
        public Dictionary<String, VariableNode> variables = null;

        public ClassNode(String name)
        {
            this.tp = NodeType.CLASS;
            this.isoutput = true;
            this.name = name;
            functions = new Dictionary<string, FunctionNode>();
            variables = new Dictionary<string, VariableNode>();
        }

        public ClassNode()
        {
            this.tp = NodeType.CLASS;
            this.name = "global";
            functions = new Dictionary<string, FunctionNode>();
            variables = new Dictionary<string, VariableNode>();
        }

        public void Add(FunctionNode item)
        {
            if (functions.ContainsKey(item.name)) return;
            functions.Add(item.name, item);
        }

        public void Add(VariableNode item)
        {
            if (variables.ContainsKey(item.name)) return;
            variables.Add(item.name, item);
        }

        public Dictionary<String, FunctionNode> getFunctions()
        {
            return this.functions;
        }

        public FunctionNode getFunction(String fname)
        {
            if (fname == null)
            {
                if (functions.ContainsKey("global")) return functions["global"];
                FunctionNode fnode = new FunctionNode("global", 0, new string[] { });
                functions.Add("global", fnode);
                return fnode;
            }

            if (functions.ContainsKey(fname)) return functions[fname];
            FunctionNode n = new FunctionNode(fname, 0, new string[] { });
            functions.Add(fname, n);
            return n;
        }

        public VariableNode getVariable(String vname)
        {
            if (variables.ContainsKey(vname)) 
                return variables[vname];
            VariableNode vnode = new VariableNode(vname);
            variables.Add(vname, vnode);
            return vnode;
        }

        public Dictionary<String, VariableNode> getVariables()
        {
            return this.variables;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder((char)name.Length + name);
            foreach (FunctionNode f in functions.Values)
            {
                sb.Append(f.ToString());
            }
            return sb.ToString();
        }
    }

    /// <summary>
    /// Trieda reprezentujuca funkcie v zdrojovych kodoch
    /// </summary>
    [Serializable]
    public class FunctionNode : DataNode
    {
        public int call = 0;
        private int paramNum = 0;
        private VariableNode[] variablePosition;
        public ClassNode inCls = null;
        public String path = null;

        /// <summary>
        /// Nazvy folmalnych parametrov funkcie
        /// </summary>
        public Dictionary<String, VariableNode> parameters = null;

        /// <summary>
        /// Zoznam volanych funkcii
        /// </summary>
        public List<FunctionNode> callfuncs = null;

        /// <summary>
        /// globalne premenne
        /// </summary>
        public Dictionary<String, VariableNode> globalVars = null;

        /// <summary>
        /// Lokalne premenne
        /// </summary>
        public Dictionary<String, VariableNode> localVars = null;

        public FunctionNode(String name, int paramNum, String[] parameters)
        {
            if (name == "")
            {
            }
            this.tp = NodeType.METHOD;
            this.name = name;
            this.paramNum = paramNum;
            variablePosition = new VariableNode[paramNum];
            globalVars = new Dictionary<string, VariableNode>();
            localVars = new Dictionary<string, VariableNode>();
            this.parameters = MakeParameters(parameters);
            this.callfuncs = new List<FunctionNode>();
        }

        public VariableNode getFormalParameter(int position)
        {
            return variablePosition[position];
        }

        public VariableNode getFormalParameter(String varname)
        {
            return parameters[varname];
        }

        private Dictionary<String, VariableNode> MakeParameters(String[] parameters)
        {
            Dictionary<String, VariableNode> res = new Dictionary<string, VariableNode>();
            for (int i = 0; i < parameters.Length; i++)
            {
                VariableNode node = new VariableNode(parameters[i]);
                node.position = i;
                variablePosition[i] = node;
                res.Add(parameters[i], node);
            }
            return res;
        }

        public bool isFormalParameter(String s)
        {
            if (parameters.ContainsKey(s))
                return true;
            return false;
        }

        public bool isGlobalVariable(string s)
        {
            if (globalVars.ContainsKey(s))
                return true;
            return false;
        }

        public VariableNode getGlobalVariable(String s)
        {
            return null;
        }

        public override string ToString()
        {
            // TO-DO: linealizacia datovej struktury
            StringBuilder sb = new StringBuilder((char)name.Length + name);
            return sb.ToString();
        }

    }

    /// <summary>
    /// Trieda reprezentujuca premennych v zdrojovych kodoch
    /// </summary>
    [Serializable]
    public class VariableNode : DataNode
    {
        public String path = null;
        private int _read, _write, _change;

        // Pozicia formalnych parametrov
        public int position = 0;
        public String varType
        {
            get;
            set;
        }

        public int read
        {
            get { return _read; }
        }

        public int write
        {
            get { return _write; }
        }

        public int change
        {
            get { return _change; }
        }

        public VariableNode(String name)
        {
#if _DEBUG_MODE_

#endif
            this.tp = NodeType.VAR;
            this.name = name;
            if (name.IndexOf('[') != -1)
            {
                this.name = name.Substring(0, name.IndexOf('['));
            }
            _read = 0;
            _write = 0;
            _change = 0;
        }

        public void Read()
        {
            this._read++;
        }

        public void Write()
        {
            this._write++;
        }

        public void Change()
        {
            this._change++;
        }
        // v pripade $var = $var + $x;
        public void ReChange()
        {
            this._write--;
            this._change++;
        }
    }
}